<?php

namespace App\Kuafu\WaiMai\Service;

use App\Kuafu\Order\Enum\CodeEnum;
use App\Kuafu\Order\Enum\OrderCodeEnum;
use App\Kuafu\Order\Exceptions\OrderException;
use App\Kuafu\WaiMai\Channel\BaseWaiMai;
use App\Kuafu\WaiMai\Enum\WaiMaiCacheEnum;
use App\Kuafu\WaiMai\Enum\WaiMaiCodeEnum;
use App\Kuafu\WaiMai\Enum\WaiMaiDBEnum;
use App\Kuafu\WaiMai\Exceptions\WaiMaiException;
use App\Kuafu\WaiMai\Model\WaiMaiOrderDetailModel;
use App\Kuafu\WaiMai\Model\WaiMaiShopPromiseSwitchModel;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

abstract class BaseWaiMaiOrderService
{
    protected int   $sourceType;
    private static  $obj;
    protected array $requestData;

    public static $callBackId = 0;
    public static $callBackShopId = 0;
    public static $errMsg = '';

    private function __construct(int $sourceType)
    {
        $this->sourceType = $sourceType;
        $this->setRequestData();
    }

    public function setRequestData(array $requestData = [])
    {
        $this->requestData = $requestData ?: \request()->all();
        return $this;
    }

    /**
     * 获取实例
     * @param int $sourceType
     * @return MtWaiMaiOrderService
     */
    public static function getIns(int $sourceType): self
    {
        if ((self::$obj[$sourceType] ?? null) instanceof static) {
            return self::$obj[$sourceType];
        }

        if ($sourceType == CodeEnum::SOURCE_TYPE_MT) {
            return self::$obj[$sourceType] = new MtWaiMaiOrderService($sourceType);
        } elseif ($sourceType == CodeEnum::SOURCE_TYPE_ELM) {
            return self::$obj[$sourceType] = new ElmWaiMaiOrderService($sourceType);
        } elseif ($sourceType == CodeEnum::SOURCE_TYPE_DY) {
            return self::$obj[$sourceType] = new DyWaiMaiOrderService($sourceType);
        }

        throw new OrderException(OrderCodeEnum::ERR_ORDER_SOURCE_TYPE_WM);
    }

    protected function bcmul100($price): int
    {
        return (int)bcmul($price, 100);
    }

    /**
     * 补单
     * @param int $shopId
     * @param int $orderId
     * @return bool
     * @throws \App\Kuafu\WaiMai\Exceptions\WaiMaiException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function repairOrder(int $shopId, int $orderId)
    {
        Log::info("Wm repairOrder start: {$orderId}");

        $oriOrder = BaseWaiMai::getIns($this->sourceType, $shopId)->getOrderDetail($orderId);
        if (!$oriOrder['status']) {
            throw new WaiMaiException(WaiMaiCodeEnum::ERR_EMPTY_ORDER_ID, $oriOrder['msg'] ?? '');
        }

        Log::info("Wm repairOrder do: {$orderId}");

        $res = $this->setRequestData($oriOrder['oriData'])->init()->createOrder();

        Log::info("Wm repairOrder end: {$orderId}");

        return $res;
    }

    /**
     * 修复订单状态
     * @param int $shopId
     * @param int $orderId
     * @param array $orderDetail
     * @return mixed
     * @throws WaiMaiException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function repairOrderStatus(int $shopId, int $orderId, array $orderDetail = [])
    {
        if (!$orderDetail || !$orderDetail['status']) {
            $orderDetail = BaseWaiMai::getIns($this->sourceType, $shopId)->getOrderDetail($orderId);
            if (!$orderDetail['status']) {
                throw new WaiMaiException(WaiMaiCodeEnum::ERR_EMPTY_ORDER_ID, $orderDetail['msg'] ?? '');
            }
        }

        return $this->setRequestData($orderDetail['oriData'])->init()->repairStatus();
    }

    // 创建订单
    abstract public function createOrder();

    // 取消订单
    abstract public function cancelOrder();

    /**
     * 菜品明细加上包装物
     * @param array $orderDetailData
     * @param int $packingPrice
     */
    protected function pushPacking(array &$orderDetailData, int $packingPrice)
    {
        $tem = $orderDetailData[0];

        $tem['productName'] = WaiMaiOrderDetailModel::PRODUCT_TYPE_2_TEXT;
        $tem['productNameStandard'] = WaiMaiOrderDetailModel::PRODUCT_TYPE_2_TEXT;
        $tem['productType'] = WaiMaiOrderDetailModel::PRODUCT_TYPE_2;
        $tem['num'] = 1;
        $tem['originalUnitPrice'] = $tem['originalPrice'] = $packingPrice;
        $tem['productProperty'] = '';
        $tem['uniqueId'] = '';
        $tem['skuId'] = '';

        $orderDetailData[] = $tem;
    }

    /**
     * 平摊菜的优惠
     * @param array $orderDetailData
     * @param int $sumDiscountPrice
     */
    protected function shareDetailDiscount(array &$orderDetailData, int $sumDiscountPrice)
    {
        $lastDiscountPrice = $sumDiscountPrice;

        $sumOriginalPrice = (int)collect($orderDetailData)->sum('originalPrice');
        if (!$sumOriginalPrice) {
            return;
        }

        $orderDetailData = collect($orderDetailData)->sortBy('originalPrice')->values()->toArray();
        foreach ($orderDetailData as $k => $v) {
            if ($v['originalPrice'] == 0) {
                $orderDetailData[$k]['payPrice']      = 0;
                $orderDetailData[$k]['discountPrice'] = 0;
                continue;
            }

            if ($k == count($orderDetailData) - 1) {
                $discountPrice = $lastDiscountPrice;
            } else {
                $discountPrice = (int)floor($v['originalPrice'] / $sumOriginalPrice * $sumDiscountPrice);
            }

            $orderDetailData[$k]['discountPrice'] = $discountPrice;
            $orderDetailData[$k]['payPrice']      = $v['originalPrice'] - $discountPrice;

            $lastDiscountPrice -= $discountPrice;
        }
    }

    /**
     * 平摊菜的收入
     * @param array $orderDetailData
     * @param int $expectIncome
     */
    protected function shareDetailIncomes(array &$orderDetailData, int $expectIncome)
    {
        $lastIncome = $expectIncome;

        $sumOriginalPrice = (int)collect($orderDetailData)->sum('originalPrice');
        if (!$sumOriginalPrice) {
            return;
        }

        $orderDetailData = collect($orderDetailData)->sortBy('originalPrice')->values()->toArray();

        foreach ($orderDetailData as $k => $v) {
            if ($v['originalPrice'] == 0) {
                $orderDetailData[$k]['incomes']         = 0;
                continue;
            }

            if ($k == count($orderDetailData) - 1) {
                $incomes = $lastIncome;
            } else {
                $incomes = (int)floor($v['originalPrice'] / $sumOriginalPrice * $expectIncome);
            }

            $orderDetailData[$k]['incomes'] = $incomes;

            $lastIncome -= $incomes;
        }
    }

    /**
     * 添加赠品
     * @param array $orderDetailData
     * @param string $productName
     * @param int $num
     * @return void
     */
    protected function addGift(array &$orderDetailData, string $productName, int $num): void
    {
        if (!$orderDetailData) {
            return;
        }

        if ($num <= 0) {
            return;
        }

        $tem = $orderDetailData[0];

        $tem['productType']         = WaiMaiOrderDetailModel::PRODUCT_TYPE_1;
        $tem['productName']         = $productName;
        $tem['productNameStandard'] = $productName;
        $tem['num']                 = $num;
        $tem['originalUnitPrice']   = $tem['originalPrice'] = $tem['discountPrice'] = 0;
        $tem['productProperty']     = '赠品';
        $tem['uniqueId']            = '';
        $tem['skuId']               = '';
        $tem['incomes']             = 0;

        $orderDetailData[] = $tem;
    }

    // 自动出餐相关操作
    protected function autoFinish(array $data)
    {
        $elmPromiseTime = 0;
        $silentSwitch   = WaiMaiDBEnum::SWITCH_ON;
        if ($this->sourceType != CodeEnum::SOURCE_TYPE_DY) {// 抖音外卖 不判断出餐开关配置

            // 总开关 - 关闭
            if (!(new WaiMaiFinishFoodService($data['shopId']))->getConfigSwitch()['switch']) {
                Log::channel('waiMai')->info("autoFinish orderId:{$data['orderId']} switch:off");

                return;
            }

            // 承诺配置
            $promiseConfig = WaiMaiShopPromiseSwitchModel::getData($data['shopId']);
            $silentSwitch = $promiseConfig['silentSwitch'] ?? WaiMaiDBEnum::SILENT_DEFAULT_SWITCH;

            // 承诺出餐的时间
            $elmPromiseSwitch = $promiseConfig['elmPromiseSwitch'] ?? WaiMaiDBEnum::ELM_PROMISE_DEFAULT_SWITCH;
            if ($elmPromiseSwitch == WaiMaiDBEnum::SWITCH_ON) {
                $elmPromiseTime = $promiseConfig['elmPromiseTime'] ?? 0;
            }

            if (!$elmPromiseTime) {
                $elmPromiseTime = (isset($promiseConfig['kfPromiseTime']) && $promiseConfig['kfPromiseTime'] > 0)
                    ? $promiseConfig['kfPromiseTime']
                    : WaiMaiDBEnum::KF_PROMISE_DEFAULT_TIME;
            }

            Log::channel('waiMai')->info("autoFinish orderId:{$data['orderId']} silentSwitch: {$silentSwitch} delaySeconds:{$elmPromiseTime}");
        }

        (new WaiMaiFinishFoodService($data['shopId']))

            // 自动出餐
            ->acceptAutoDelayFinish($data['orderId'], $data['originalPrice'], $data['expectFast'], $data['expectTime'], $silentSwitch)

            // 预约单备餐提醒
            ->acceptAutoDelayPrepare($data['orderId'], $data['expectFast'], $data['expectTime'])

            // 兜底的强制出餐
            ->lastForceAutoFinish($data['orderId'], $elmPromiseTime, $silentSwitch);
    }

    /**
     * 获取方法锁
     * @param string $orderId
     * @param string $funcName
     * @return bool
     */
    protected function lockFuncBefore(string $orderId, string $funcName): bool
    {
        $redis_key = WaiMaiCacheEnum::getFuncLock($orderId, $funcName);

        if (!Redis::setnx($redis_key, time())) {

            // 防止死锁
            $lock_time = Redis::get($redis_key);
            if (($lock_time + 10) < time()) {
                Redis::del($redis_key);
            } else {
                Log::warning("lockFuncBefore jam {$funcName} key: {$redis_key}");
                throw new WaiMaiException(WaiMaiCodeEnum::ERR_CREATE_ORDER_JAM);
            }
        }

        Redis::expire($redis_key, 5);

        return true;
    }

    /**
     * 删除方法锁
     * @param string $orderId
     * @param string $funcName
     * @return void
     */
    protected function lockFuncAfter(string $orderId, string $funcName): void
    {
        $redis_key = WaiMaiCacheEnum::getFuncLock($orderId, $funcName);
        Redis::del($redis_key);
    }
}
